package com.xiaobaibai.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaobaibai.bo.OrderDelayItem;
import com.xiaobaibai.common.CouponEnum;
import com.xiaobaibai.common.OrderCommon;
import com.xiaobaibai.common.OrderEnum;
import com.xiaobaibai.dao.OrderDao;
import com.xiaobaibai.entity.*;
import com.xiaobaibai.service.*;
import com.xiaobaibai.threadPool.MyThreadPoolUnit;
import com.xiaobaibai.units.*;
import com.xiaobaibai.units.pay.BigDecimalUtil;
import com.xiaobaibai.vo.CreateOrderItemVo;
import com.xiaobaibai.vo.CreateOrderVo;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xiaobaibai
 * @since 2020-10-16
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderDao, Order> implements IOrderService {

    @Autowired
    private IBargainUserService bargainUserService;

    @Autowired
    private ICouponService couponService;

    @Autowired
    private ICouponBelongService couponBelongService;

    @Autowired
    private IUserAddressService userAddressService;

    @Autowired
    private IProductService productService;

    @Autowired
    private IOrderItemService orderItemService;

    @Autowired
    private OrderNoPayDelayQueueUnit orderNoPayDelayQueueUnit;

    @Autowired
    private OrderWXNewsUnit orderWXNewsUnit;

    @Autowired
    private IUserInfoService userInfoService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IUserIntegralDetailService userIntegralDetailService;

    @Autowired
    private IDistributionUserService distributionUserService;

    @Autowired
    private IDistributionRecordingService distributionRecordingService;

    @Autowired
    private ProductEvaluationDelayQueueUnit productEvaluationDelayQueueUnit;

    @Value("${orderShipping}")
    private Integer orderShipping;//运费

    @Value("${threshold}")
    private Integer threshold;//免运费门槛

    @Value("${distributionProportion}")
    private Double distributionProportion;//分销商得到的比例

    @Value("${autoEvaluationHour}")
    private Integer autoEvaluationHour;//自动评价小时

    @Value("${autoCancelOrderSecond}")
    private Integer autoCancelOrderSecond;//待支付自动取消订单

    @Override
    @Transactional//开启事务
    public String createOrder(CreateOrderVo createOrderVo,Integer userId) {
        /**
         * 1.先检验优惠券
         * 2.再检验砍价商品
         * 3.再检验地址
         * 4.最后检验商品,顺便计算好价格
         */
        //查询会员消费金额达到1000才可以货到付款
        if(createOrderVo.getFaceToFace()) {
            QueryWrapper<UserInfo> q0 = new QueryWrapper<>();
            q0.select("user_amount");
            q0.eq("user_id", userId);
            UserInfo user = userInfoService.getOne(q0);
            if (user.getUserAmount().intValue() < 1000) {
                throw new RuntimeException("非会员不可货到付款");
            }
        }

        StringBuilder productNameWxNews=new StringBuilder();
        Date date = new Date();
        String orderId= OrderIdUnit.createOrderId(userId);
        Coupon coupon = null;
        CouponBelong couponBelong = null;
        Integer couponId = createOrderVo.getCouponId();
        if(couponId!=null){
            QueryWrapper<CouponBelong> q1=new QueryWrapper<>();
            q1.select("id","user_id","coupon_id","status");
            q1.eq("user_id",userId);
            q1.eq("coupon_id",createOrderVo.getCouponId());
            couponBelong = couponBelongService.getOne(q1);
            if(couponBelong==null)
                throw new RuntimeException("使用的优惠券不存在");
            if(couponBelong.getStatus()!=0)
                throw new RuntimeException("使用的优惠券已失效");//或者已经使用了
            QueryWrapper<Coupon> q2=new QueryWrapper<>();
            q2.select("coupon_type","coupon_price","coupon_threshold","coupon_discount","coupon_invalidation_time");
            q2.eq("id",createOrderVo.getCouponId());
            coupon = couponService.getOne(q2);
            if(coupon.getCouponInvalidationTime().before(date))//过期时间在现在之前
                throw new RuntimeException("优惠券已经过期");
        }
        //砍价商品
        Integer bargainId = createOrderVo.getBargainId();
        BargainUser bargainUser = null;
        Product bargainProduct=null;
        if(bargainId!=null){
            QueryWrapper<BargainUser> q1=new QueryWrapper<>();
            q1.select("id","product_id","bargain_progress","status","failure_time");
            q1.eq("user_id",userId);
            q1.eq("id",createOrderVo.getBargainId());
            bargainUser = bargainUserService.getOne(q1);
            if(bargainUser==null)
                throw new RuntimeException("您未有砍价商品");
            if(bargainUser.getBargainProgress()<100)
                throw new RuntimeException("砍价商品未达标不可获取");
            if(bargainUser.getStatus()!=2)
                throw new RuntimeException("砍价商品不可重复兑换");
            /**
             * 过期了也可以兑换,因为包含了砍价成功后未24小数内兑换的
             */
            //通过检验,去获取商品信息
            QueryWrapper<Product> q2=new QueryWrapper<>();
            q2.select("id","product_price");//获取价格和id
            q2.eq("id",bargainUser.getProductId());
            Product product = productService.getOne(q2);
            if(product==null)
                throw new RuntimeException("请联系老板,砍价商品不存在");
            bargainProduct=product;
        }
        //用户地址
        Integer addressId = createOrderVo.getAddressId();
        if(addressId==null){
            throw new RuntimeException("请指定配置地址");
        }
        QueryWrapper<UserAddress> queryWrapper=new QueryWrapper<>();
        queryWrapper.select("user_name","user_phone","user_address_detail");
        queryWrapper.eq("user_id",userId);
        queryWrapper.eq("id",addressId);
        UserAddress userAddress = userAddressService.getOne(queryWrapper);
        if(userAddress==null)
            throw new RuntimeException("配置地址不存在,请重新指定配送地址");

        //检验商品,计算好总价格
        List<CreateOrderItemVo> list = createOrderVo.getList();
        /**
         * 一件件的去遍历选中的商品,一件件的减库存
         */
        List<OrderItem> orderItemList=new ArrayList<>();//订单子项
        BigDecimal sum=new BigDecimal(0);//订单总价
//        BigDecimal discountSum=new BigDecimal(0);//原与优惠之间的差价和
        for (CreateOrderItemVo item : list) {

            int index=0;
            Product product = null;
            while (true){//重试3次
                index++;

                QueryWrapper<Product> q1=new QueryWrapper<>();
                q1.eq("id",item.getProductId());
                q1.eq("product_status",true);//必须是上架状态
                q1.select("id","product_name","product_sales","product_stock","product_old_price","product_price","product_version");
                product = productService.getOne(q1);

                if(product==null)
                    throw new RuntimeException("商品不存在");//或者下架了
                if(product.getProductStock()-item.getCount()<0)
                    throw new RuntimeException("商品库存不足");

                productNameWxNews.append(product.getProductName());//微信消息模板
                //库存不足->提醒老板
                if(product.getProductStock()-item.getCount()<=10){
                    SmsUnit.insufficientStock(product.getId(),product.getProductName());
                }

                //减库存
                product.setProductStock(product.getProductStock()-item.getCount());
                //加销量
                product.setProductSales(product.getProductSales()+item.getCount());

                //修改商品后入库
                boolean flag = productService.updateById(product);
                if(!flag) {//乐观锁修改失败
                    if(index>=3)//修改三次都失败了
                        throw new RuntimeException("高峰期,订单系统繁忙,请几秒后再试");
                }else{
                    break;//修改成功就break
                }

            }

            //计算优惠金额
//            discountSum=BigDecimalUtil
//                    .add(discountSum.doubleValue(),
//                            BigDecimalUtil.sub(product.getProductPrice().doubleValue(),
//                                    product.getProductOldPrice().doubleValue()).doubleValue());
            OrderItem orderItem=new OrderItem();
            orderItem.setOrderId(orderId);
            orderItem.setProductId(product.getId());
            orderItem.setProductPrice(product.getProductPrice());
            orderItem.setProductCount(item.getCount());
            BigDecimal itemSum =
                    BigDecimalUtil.mul(product.getProductPrice().doubleValue(), item.getCount().doubleValue());
            orderItem.setItemPrice(itemSum);//子订单总价
            orderItemList.add(orderItem);//放入集合
            sum = BigDecimalUtil.add(sum.doubleValue(), itemSum.doubleValue());//累加订单总价
        }

        BigDecimal originalSum=new BigDecimal(sum.doubleValue());//原价钱
        //结算优惠券并修改优惠券状态
        if(coupon!=null){
            //修改优惠券状态
            couponBelong.setStatus(2);//设置已经使用
            couponBelong.setOrderId(orderId);
            couponBelong.setUserTime(date);
            couponBelongService.updateById(couponBelong);//修改
            //结算优惠券
            if(coupon.getCouponThreshold()>sum.doubleValue())
                throw new RuntimeException("优惠券门槛未达标");//门槛不够
            if(coupon.getCouponType().equals(CouponEnum.MANJIAN.getCode())){
                //满减券,直接减
                sum=BigDecimalUtil.sub(sum.doubleValue(),coupon.getCouponPrice().doubleValue());
            }else if(coupon.getCouponType().equals(CouponEnum.ZHEKOU.getCode())){
                //折扣券,打折
                sum=BigDecimalUtil.mul(sum.doubleValue(),0.1*coupon.getCouponDiscount().doubleValue());
            }else{
                throw new RuntimeException("优惠券未知错误,请联系老板");
            }
        }
        //结算砍价商品,并且修改砍价商品状态
        if(bargainProduct!=null){
            //修改砍价商品状态
            bargainUser.setStatus(3);//设置成已经兑换
            bargainUserService.updateById(bargainUser);
            //结算砍价商品
            OrderItem orderItem=new OrderItem();
            orderItem.setOrderId(orderId);
            orderItem.setProductId(bargainProduct.getId());
            orderItem.setProductPrice(bargainProduct.getProductPrice());
            orderItem.setProductCount(1);
            orderItem.setItemPrice(new BigDecimal(0));//砍价商品0元
            orderItemList.add(orderItem);//放入订单集合
        }
        //大订单入库
        Order order=new Order();
        order.setOrderId(orderId);
        order.setUserId(userId);
        order.setUserName(userAddress.getUserName());
        order.setUserPhone(userAddress.getUserPhone());
        order.setUserAddress(userAddress.getUserAddressDetail());
        order.setUserRemarks(createOrderVo.getRemarks());
        order.setOrderPrice(originalSum);//订单原价
        if(originalSum.intValue()<threshold){
            //实付金额小于免运费门槛,那么就需要增加运费
            order.setOrderShipping(orderShipping);//增加运费
            originalSum = BigDecimalUtil.add(originalSum.doubleValue(), orderShipping);//运费加到总金额上去
        }
        if(coupon!=null)
            order.setOrderCouponId(couponId);
        if(bargainProduct!=null)
            order.setBargainUserId(createOrderVo.getBargainId());
        //减去打折后得到的优惠
        BigDecimal sub = BigDecimalUtil.sub(originalSum.doubleValue(), sum.doubleValue());
        //再加上每个商品优惠最后得到总优惠赋入
        order.setOrderReduceAmount
                (sub);//优惠金额
        if(sum.doubleValue()<=0)
            sum=new BigDecimal(0.01);//最低付款0.01元
        order.setOrderRealPrice(sum);//实付金额
        if(createOrderVo.getFaceToFace())
            order.setOrderStatus(OrderEnum.FACETO.getCode());
        else
            order.setOrderStatus(OrderEnum.NOPAY.getCode());//等待支付状态
        boolean orderFlag = this.save(order);//入库
        if(!orderFlag)
            throw new RuntimeException("大订单入库失败,请联系老板");

        //小定单入库
        boolean orderItemFlag = orderItemService.saveBatch(orderItemList);//入库
        if(!orderItemFlag)
            throw new RuntimeException("小订单入库失败,请联系老板");


        OrderDelayItem orderDelayItem=new OrderDelayItem(orderId,DateUtils.addSeconds(date,autoCancelOrderSecond));//14分40秒
        orderNoPayDelayQueueUnit.saveOrderDelayItem(orderDelayItem);//微信支付订单需要加入延时队列

        if(createOrderVo.getFaceToFace()) {//仅货到付款才发送微信通知
            orderWXNewsUnit.sendOkOrderNew(userAddress.getUserPhone(),userId,orderId,productNameWxNews.toString(),
                    userAddress.getUserAddressDetail());
        }

        return order.getOrderId();//返回订单号
    }

    @Override
    @Transactional
    public boolean cancelOrder(String orderId) {
        Order order = this.getById(orderId);
        //自动取消这里加一个锁,防止用户现在去付款,
        stringRedisTemplate.opsForValue().set(OrderCommon.doOrderLock+order.getUserId(),"1",5, TimeUnit.SECONDS);
        if(!order.getOrderStatus().equals(OrderEnum.NOPAY.getCode())){
            //非待支付状态,就不管了
            return true;
        }
        Integer userId=order.getUserId();
        Integer orderCouponId = order.getOrderCouponId();
        if(orderCouponId!=null){
            //返还优惠券
            UpdateWrapper<CouponBelong> u1=new UpdateWrapper<>();
            u1.eq("user_id",userId);
            u1.eq("coupon_id",orderCouponId);
            u1.set("status",0);//转为未使用
            couponBelongService.update(u1);//返还优惠券
        }
        Integer bargainUserId = order.getBargainUserId();
        if(bargainUserId!=null){
            //返还砍价商品
            UpdateWrapper<BargainUser> u2=new UpdateWrapper<>();
            u2.eq("id",order.getBargainUserId());
            u2.set("status",2);
            bargainUserService.update(u2);//返还砍价商品
        }
        productService.returnStock(orderId);
        //修改状态并且存入数据库
        //不用本类方法,防止发送异常不会回滚(但我发现,即使调用本类this.updateById出错也会回滚)
        int i = this.baseMapper.updateOrderStatus(order.getOrderId(), OrderEnum.CANCEL.getCode());
        if(i==0){
            //修改时不是待支付的订单了,说明用户碰巧冲突支付了
            throw new RuntimeException("自动取消时变为非待支付订单了");
        }
        return true;
    }

    @Override
    public List<Order> getMyOrderList(Integer pageNum,Integer pageSize,Integer userId) {
        pageNum=pageNum*pageSize;
        return this.baseMapper.getMyOrderList(pageNum,pageSize,userId);
    }

    @Override
    public void confirmGoods(Order order) {
        UpdateWrapper<Order> u1=new UpdateWrapper<>();
        u1.eq("order_id",order.getOrderId()).eq("order_status",order.getOrderStatus());
        u1.set("order_status",OrderEnum.OK.getCode());
        boolean update = this.update(u1);
        if(!update) {//如果修改失败,就原地停止
            log.error("订单确认收货失败,订单号:"+order.getOrderId());
            return;
        }
        CompletableFuture.runAsync(()->{
            //用户杂七杂八修改  消费金额,订单数量,最后一次下单时间,订单积分
            int integral = order.getOrderRealPrice().intValue();//积分金额 1:1 的转换
            QueryWrapper<UserInfo> q2=new QueryWrapper<>();
            q2.eq("user_id",order.getUserId());
            q2.select("id","user_amount","user_order_quantity","user_integral");
            UserInfo userInfo = userInfoService.getOne(q2);
            userInfo.setUserAmount(BigDecimalUtil
                    .add(userInfo.getUserAmount().doubleValue(),order.getOrderRealPrice().doubleValue()));
            userInfo.setUserOrderQuantity(userInfo.getUserOrderQuantity()+1);
            userInfo.setUserLastBuy(order.getCreateTime());
            userInfo.setUserIntegral(userInfo.getUserIntegral()+integral);
            userInfoService.updateById(userInfo);//保存
            //积分记录表
            UserIntegralDetail userIntegralDetail=new UserIntegralDetail();
            userIntegralDetail.setIntegralContent("购物返还积分");
            userIntegralDetail.setIntegralFlag(true);
            userIntegralDetail.setUserId(order.getUserId());
            userIntegralDetail.setIntegralQuantity(integral);
            userIntegralDetailService.save(userIntegralDetail);//保存


            orderWXNewsUnit.sendCompeteOrder(order.getUserPhone(),order.getUserId(),order.getOrderId());

            String nowDay = Last7DaysUtil.getNowDay();
            stringRedisTemplate.execute(new SessionCallback<Object>() {
                @Override
                public Object execute(RedisOperations redisOperations) throws DataAccessException {
                    //放入用户的待评价缓存集合里面
                    redisOperations.opsForList().leftPush(OrderCommon.waitEvaluationOrder +order.getUserId(),order.getOrderId());
                    redisOperations.expire(OrderCommon.waitEvaluationOrder+order.getUserId(),7, TimeUnit.DAYS);
                    //新增今日完成订单数量+今日销售额,只能获取近七天的数据
                    redisOperations.opsForValue().increment(OrderCommon.orderCount+nowDay,1);
                    redisOperations.opsForValue().increment(OrderCommon.orderAmount+nowDay,order.getOrderRealPrice().intValue());
                    return null;
                }
            });

            productEvaluationDelayQueueUnit.saveEvaluationDelayItem(
                    new OrderDelayItem(order.getOrderId(),DateUtils.addHours(new Date(),autoEvaluationHour)));
            //加上对应的分销金额
            /**
             * 1.是否加入了分销商
             * 2.是否有super邀请码
             * 3.上级金额+10% 增加记录
             */
            Integer userId = order.getUserId();
            QueryWrapper<DistributionUser> q3=new QueryWrapper<>();
            q3.select("id","superior_code");
            q3.eq("user_id",userId);
            DistributionUser distributionUser = distributionUserService.getOne(q3);
            if(distributionUser!=null){
                //不为空才行动
                BigDecimal orderRealPrice = order.getOrderRealPrice();
                BigDecimal resultAmount = BigDecimalUtil.mul(orderRealPrice.doubleValue(), distributionProportion);
                QueryWrapper<DistributionUser> q4=new QueryWrapper<>();
                q4.select("id","amount");
                q4.eq("invitation_code",distributionUser.getSuperiorCode());
                DistributionUser distributionUserParent = distributionUserService.getOne(q4);
                distributionUserParent.setAmount(BigDecimalUtil
                        .add(distributionUserParent.getAmount().doubleValue(),resultAmount.doubleValue()));
                distributionUserService.updateById(distributionUserParent);
                DistributionRecording distributionRecording=new DistributionRecording();
                distributionRecording.setOrderId(order.getOrderId());
                distributionRecording.setAmount(resultAmount);
                distributionRecording.setDistributionId(distributionUserParent.getId());
                distributionRecordingService.save(distributionRecording);
            }

        }, MyThreadPoolUnit.threadPoolExecutor);
    }


}
